/**
 * 
 */
package de.hft_stuttgart.dsa.ctt.processing.yuruz;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import de.hft_stuttgart.dsa.ctt.model.AbstractMatrix;
import de.hft_stuttgart.dsa.ctt.model.Course;
import de.hft_stuttgart.dsa.ctt.model.CourseAssignmentMatrix;
import de.hft_stuttgart.dsa.ctt.model.ProblemInstance;
import de.hft_stuttgart.dsa.ctt.processing.IAssigner;

/**
 * @author Ali Yuruz
 */
public class YuruzAssigner implements IAssigner {

	private ProblemInstance problemInstance;
	private AbstractMatrix matrix;
	private AbstractMatrix tmpMatrix;
	private ConstraintChecker constraintChecker;
	private CurriculumChecker curriculumChecker;
	private List<Course> coursesFromLastIteration = new ArrayList<Course>();
	private static final int MAX_ITERATIONS = 100;
	private HashMap<String, Integer> coursesAndNumberofLectures;
	private CourseAssignmentMatrix courseAssignmentMatrix;
	private List<Course> coursesToAssign;
	private HashMap<String, Course> coursesAsMap;

	public YuruzAssigner(ProblemInstance problemInstance, AbstractMatrix matrix) {
		this.problemInstance = problemInstance;
		this.matrix = matrix;
		try {
			this.tmpMatrix = matrix.clone();
		} catch (CloneNotSupportedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		coursesToAssign = new ArrayList<Course>();
		coursesToAssign.addAll(problemInstance.getPriorityBasedCourses());

		courseAssignmentMatrix = new CourseAssignmentMatrix(problemInstance.getRooms(), problemInstance.getPeriods());
		coursesAsMap = problemInstance.getAllCourses();
	}

	@Override
	public CourseAssignmentMatrix assignCoursesToMatrix() {
		constraintChecker = new ConstraintChecker(problemInstance);
		curriculumChecker = new CurriculumChecker(problemInstance);
		coursesAndNumberofLectures = createCourseNrMap();

		int selectedSlotIndex = -1;
		int selectedRoomIndex = -1;
		int loopInvariant = 0;
		int innerInvariant = 0;
		Course selectedCourse;
		do {
			while (!coursesAndNumberofLectures.isEmpty() && innerInvariant <= MAX_ITERATIONS) {
				selectedCourse = selectCourse();
				int[] slotAndRaum = selectSlotAndRaum(selectedCourse);
				selectedSlotIndex = slotAndRaum[0];
				selectedRoomIndex = slotAndRaum[1];
				if (-1 != selectedSlotIndex && -1 != selectedRoomIndex) {
					assignToMatrix(selectedCourse, selectedSlotIndex, selectedRoomIndex);
				} else {
					System.out.println("Nicht assignable: " + selectedCourse.getCourseId());
					coursesFromLastIteration.add(selectedCourse);
				}

				innerInvariant++;
			}
			innerInvariant = 0;
			loopInvariant++;
		} while (coursesFromLastIteration.isEmpty() && loopInvariant <= MAX_ITERATIONS);

		return courseAssignmentMatrix;
	}

	private void assignToMatrix(Course selectedCourse, int selectedSlotIndex, int selectedRoomIndex) {
		Course[][] courseMatrix = (Course[][]) courseAssignmentMatrix.getPeriodRoomMatrix();
		courseMatrix[selectedSlotIndex][selectedRoomIndex] = selectedCourse;
	}

	private HashMap<String, Integer> createCourseNrMap() {
		HashMap<String, Integer> map = new HashMap<String, Integer>();
		for (Course course : problemInstance.getAllCourses().values()) {
			map.put(course.getCourseId(), course.getNumberOfLectures());
		}
		return map;
	}

	/**
	 * @param selectedCourse
	 * @return An array with two elements <br>
	 *         1. Period<br>
	 *         2. RoomNr.
	 */
	private int[] selectSlotAndRaum(Course selectedCourse) {
		int selectedPeriodSlot = -1;
		int roomIndex = -1;
		do {
			selectedPeriodSlot = (int) (Math.random() * courseAssignmentMatrix.getPeriodRoomMatrix().length);
			System.out.println(selectedPeriodSlot + " do selectSlotAndRaum, course :" + selectedCourse.getCourseId());
		} while (!curriculumChecker.isAssignable(selectedCourse, courseAssignmentMatrix, selectedPeriodSlot)
				|| !constraintChecker.isAssignable(selectedCourse, courseAssignmentMatrix, selectedPeriodSlot));

		if (-1 == selectedPeriodSlot)
			return new int[] { -1, -1 };

		ArrayList<Integer> possibleRoomNr = new ArrayList<Integer>();
		int i = 0;
		for (Object courseObject : courseAssignmentMatrix.getPeriodRoomMatrix()[selectedPeriodSlot]) {
			if (null == ((Course) courseObject).getCourseId()) {
				possibleRoomNr.add(i);
			}
			i++;

		}
		if (!possibleRoomNr.isEmpty()) {
			roomIndex = possibleRoomNr.get((int) Math.random() * possibleRoomNr.size());
		}
		return new int[] { selectedPeriodSlot, roomIndex };
	}

	private Course selectCourse() {
		int randomSize = getRemainingCourseNr();
		int randomNr = (int) (Math.random() * randomSize);
		for (String lectureKey : coursesAndNumberofLectures.keySet()) {
			randomNr = randomNr - coursesAndNumberofLectures.get(lectureKey);

			if (randomNr <= 0) {
				return coursesAsMap.get(lectureKey);
			}
		}
		return null;
	}

	private int getRemainingCourseNr() {
		int remainingCourses = 0;
		for (int remainingLecture : coursesAndNumberofLectures.values()) {
			remainingCourses += remainingLecture;
		}
		return remainingCourses;
	}

}
